
import cherrypy
import random
import math
import json
import os.path
import py.geobox as geobox
from string import Template
MAP_LEFT = 50
MAP_TOP = 50
MAP_RIGHT = 900
MAP_BOTTOM = 400
#test

def get_distance2(x1,x2,y1,y2):
    return math.pow(x2-x1,2) + math.pow(y2-y1,2)
    
class Server:
    item_counter = 0
    player_counter = 0
    players = {}
    ships = {}
    space_items = {}
    space_item_deletes = []
    actions = []
    turn_counter = 0
    action_log = []
    data_by_player = {}
    geoindex = geobox.geobox()
    
    @cherrypy.expose
    def create_new_player(self):
        Server.player_counter += 1
        player = Server.player_counter
        Server.players[str(player)] = player
        Server.data_by_player[player] = {}
        Server.data_by_player[player]['view'] = {}
        x = random.randint(MAP_LEFT,MAP_RIGHT)
        y = random.randint(MAP_TOP,MAP_BOTTOM)
        a = {'type':'create','what':'base','player':player,'x':x,'y':y,'start':True}
        Server.actions.append(a)
        return json.dumps({'player_number':player})
    
    def start_player(self):
        Server.player_counter += 1
        player = Server.player_counter
        Server.data_by_player[player] = {}
        Server.data_by_player[player]['view'] = {}
        x = random.randint(MAP_LEFT,MAP_RIGHT)
        y = random.randint(MAP_TOP,MAP_BOTTOM)
        a = {'type':'create','what':'base','player':player,'x':x,'y':y,'start':True}
        Server.actions.append(a)
        return player
    
    @cherrypy.expose
    def get_game_info(self):
        return json.dumps({'turn':Server.turn_counter,
                           'space_items':Server.space_items,
                           'actions':Server.actions})
    
    @cherrypy.expose 
    def get_turn(self):
        return json.dumps({'turn':Server.turn_counter})
    
    @cherrypy.expose
    def get_player_data(self,player_idx):
        #test
        return json.dumps(Server.data_by_player[player_idx])
    
    @cherrypy.expose
    def get_player_view(self,player_number):
        return json.dumps({'turn': Server.turn_counter,
                           'actions':[],
                           'view':Server.data_by_player[int(player_number)]['view']})
        
    @cherrypy.expose
    def get_player_view_loggedin(self):
        id = cherrypy.session.get('player_id')
        return json.dumps({'turn': Server.turn_counter,
                           'actions':[],
                           'player_id':id,
                           'view':Server.data_by_player[id]['view']})
    
    @cherrypy.expose
    def get_geobox(self):
        return json.dumps(Server.geoindex.index)
    
    def space_item_filter(self,term,value):
        matched_items = []
        for i in Server.space_items.values():
            if i.has_key(term):
                if i[term] == value:
                    matched_items.append(i)
        return matched_items
    
    @cherrypy.expose
    def get_action_log(self):
        html_log = '<br>'.join(Server.action_log)
        return html_log
    
    @cherrypy.expose
    def item_filter(self,term,value,valuetype='string'):
        if valuetype == 'int':
            value = int(value)
        matched_items = self.space_item_filter(term,value)
        return json.dumps(matched_items)
    
    @cherrypy.expose
    def get_all_space_items(self):
        return json.dumps(Server.space_items.values(),indent=4)

    def do_move(self,a):
        ship = Server.space_items[a['idx']]
        ship_speed2 = math.pow(ship['speed'],2)
        distance2 = get_distance2(ship['x'],a['x'],ship['y'],a['y'])
        if distance2 <= ship_speed2 and ship['ep'] >= 10:
            ship['x'] = a['x']
            ship['y'] = a['y']
            ship['ep'] -= 10
            l = 'MOVE committed %s' % json.dumps(ship)
            Server.action_log.append(l)
            
        else:
            l = 'ERROR move not committed distance2 = %s speed2 = %s' % (distance2,ship_speed2)
            Server.action_log.append(l)
            
    def do_create(self,a):
        Server.item_counter += 1
        idx = Server.item_counter
        t = {}
        if a['what'] == 'base' and a.has_key('start'):
            t = {'type':'base',
                 'player':a['player'],
                 'idx':idx,
                 'x':a['x'],
                 'y':a['y'],
                 'sp':1000,
                 'ore':100}
            
            Server.space_items[idx] = t
            return True
            
        base = a['base']
        ore = Server.space_items[base]
        if a['what'] == 'ship' and ore > 10:
            t = {'idx':idx,
                 'type':'ship',
                 'x': a['x'],
                 'y': a['y'],
                 'sp':100,
                 'ep':100,
                 'speed':10,
                 'range':10,
                 'regen':10,
                 'player':a['player']}
            
            Server.space_items[base]['ore'] -= 10
            Server.space_items[idx] = t
            
        elif a['what'] == 'base' and ore > 1000:
            t = {'type':'base',
                 'player':a['player'],
                 'idx':idx,
                 'x':a['x'],
                 'y':a['y'],
                 'sp':1000,
                 'ore':100}
            
            Server.space_items[base]['ore'] -= 1000
            Server.space_items[idx] = t
            

        
    def do_attack(self,a):
        atk = Server.space_items[a['atk']]
        tgt = Server.space_items[a['tgt']]
        distance2 = get_distance2(atk['x'],tgt['x'],atk['y'],tgt['y'])
        atk_range2 = math.pow(atk['range'],2)
        if distance2 < atk_range2 and atk['ep'] >= 10:
            tgt['sp'] -= 10
            atk['ep'] -= 10
            
            l = 'ATTACK %s [ep:%s] attacks %s [sp:%s]' % (atk['idx'],atk['ep'],tgt['idx'],tgt['sp'])
            Server.action_log.append(l)
            
            if tgt['sp'] <= 0:
                Server.space_item_deletes.append(tgt['idx'])
                l = 'DESTROY %s killed %s' % (atk['idx'],tgt['idx'])
                Server.action_log.append(l)
            
    def do_mine(self,a):
        miner = Server.space_items[a['miner']]
        asteroid = Server.space_items[a['asteroid']]
        if asteroid['type'] == 'asteroid':
            distance2 = get_distance2(miner['x'],asteroid['x'],miner['y'],asteroid['y'])
            mine_range2 = math.pow(miner['range'],2)

                
            if distance2 <= mine_range2 and miner['ep'] >= 10:
                l = 'MINE asteroid %s' % a['asteroid']
                Server.action_log.append(l)
        
                miner['ep'] -= 10
                #change later to ore drop off
                Server.space_items[a['base']]['ore'] += 10
                Server.space_item_deletes.append(a['asteroid'])
                
            else:
                l = 'too far to mine %s' % distance2
                Server.action_log.append(l)
                
        
    def __init__(self):
        self.action_dict = {'move':self.do_move,
                            'attack':self.do_attack,
                            'mine':self.do_mine,
                            'create':self.do_create}
        
        self.make_asteroids(90)

    @cherrypy.expose
    def index(self):
        html = open(os.path.join(os.path.dirname(__file__), 'html/index.html'),"r")
        return html.read()
    
    @cherrypy.expose
    def stats(self):
        html = open(os.path.join(os.path.dirname(__file__), 'html/stats.html'),"r")
        return html.read()
    
    @cherrypy.expose
    def login(self):
        html = open(os.path.join(os.path.dirname(__file__), 'html/login.html'),"r")
        return html.read()
    
    @cherrypy.expose
    def do_login(self, username=None, password=None):
        if (username == password):
            if Server.players.has_key(username):
                cherrypy.session['player_id'] = Server.players['username']
                cherrypy.session['player_name'] = username
                cherrypy.session['log'] = 'true'
            else:
                id = self.start_player()
                Server.players['username'] = id
                cherrypy.session['player_id'] = id
                cherrypy.session['player_name'] = username
                cherrypy.session['log'] = 'true'
            raise cherrypy.HTTPRedirect('player_page')
        else:
            raise cherrypy.HTTPRedirect('login')
        
    @cherrypy.expose
    def player_page(self):
        if cherrypy.session.get('log') == 'true':
            html = open(os.path.join(os.path.dirname(__file__), 'html/player.html'),"r").read()
            d = {'playername':cherrypy.session.get('player_name'),
                 'id':cherrypy.session.get('player_id')}
            temp = Template(html).substitute(d)
            return temp
        else:
            raise cherrypy.HTTPRedirect('login')


    
    @cherrypy.expose
    def test_json(self,stuff):
        a = json.loads(stuff)
        return json.dumps(a)

    def make_asteroids(self,amount):
        for i in range(0,amount):
            Server.item_counter += 1
            idx = Server.item_counter
            a = {'type':'asteroid',
                 'x':random.randint(MAP_LEFT,MAP_RIGHT),
                 'y':random.randint(MAP_TOP,MAP_BOTTOM),
                 'idx':idx}
            
            Server.space_items[idx] = a
    
    @cherrypy.expose
    def submit_actions(self,actions=[]):
        acts = json.loads(actions)
        Server.actions.extend(acts)
        return 'got %s actions' % str(len(acts))

    @cherrypy.expose
    def get_actions(self):
        return json.dumps(Server.actions)
    
    @cherrypy.expose    
    def do_actions(self):
        self.clear_index()
        
        old_len = len(Server.actions)
        
        #calculate each action
        while len(Server.actions) > 0:
            a = Server.actions.pop()
            self.action_dict[a['type']](a)
            
        self.do_deletes()
        self.make_asteroids(random.randint(1,10))
        
        #do the regen for each item and add each item to the indexes
        for i in Server.space_items.values():
            if i.has_key('ep'):
                i['ep'] += i['regen']
            self.add_item_to_index(i)

        #calculate the views for each player and cache them
        for p in Server.data_by_player.keys():
            view = self.calc_player_view(p)
            Server.data_by_player[p]['view'] = view
            
        #after all accounting is done increment the turn counter
        Server.turn_counter += 1
            
        return 'did them %s .... action list len is %s' % (old_len,len(Server.actions))

    def do_deletes(self):
        while len(Server.space_item_deletes) > 0:
            idx = Server.space_item_deletes.pop()
            if Server.space_items.has_key(idx):
                del Server.space_items[idx]
                
        
    def add_item_to_index(self,item):
        t = item['type']
        x = item['x']
        y = item['y']
            
        if item.has_key('player'):
            p = item['player']
            Server.data_by_player[p].setdefault(t,[]).append(item)
            
        Server.geoindex.add_point(x,y,item)
        
    def calc_player_view(self,player_number):
        cells = []
        items = {}
        for i in Server.data_by_player[player_number]['ship']:
            cells.append(Server.geoindex.get_cell(i['x'],i['y']))
            
        for i in Server.data_by_player[player_number]['base']:
            cells.append(Server.geoindex.get_cell(i['x'],i['y']))
            
        set_cells = set(cells)
        
        for c in set_cells:
            near_bys = Server.geoindex.get_near_by(cell=c)
            for n in near_bys:
                items[n['idx']] = n
                
        return items
            
    def clear_index(self):
        for p in Server.data_by_player.keys():
            Server.data_by_player[p] = {}
            Server.data_by_player[p]['ship'] = []
            Server.data_by_player[p]['base'] = []
            Server.data_by_player[p]['view'] = []
        Server.geoindex.clear()
        


prodconf = os.path.join(os.path.dirname(__file__), 'prod.conf')
current_dir = os.path.dirname(os.path.abspath(__file__))

if __name__ == '__main__':
    cherrypy.quickstart(Server(), config=prodconf)

